package plgutils

import (
	"dgo/MyProj/PrjHttpClt"
	"dgo/MyProj/PrjHttpUtils"
	"dgo/goutils/utils"
	"dgo/plg/plgnet"
	"fmt"
	"gitee.com/ymofen/gobase"
	"gitee.com/ymofen/supervalue"
	"os"
	"strings"
	"time"
)

/*
**

	自定义区域管理
*/
type GisRegionPlg struct {
	PlgBase
	md5sumstr  string
	msgShowLvl int8
	msglst     *utils.SyncCycleList

	regionlst utils.GSyncMapGroup

	url             string
	localfile       string
	last_warn       string
	last_reload_msg string
}

func (this *GisRegionPlg) GetRegionlst(regtype string) []*GisRegionItem {
	rval := make([]*GisRegionItem, 0, this.regionlst.Count())
	this.regionlst.ListRangeV2(func(key, value interface{}) bool {
		return len(regtype) == 0 || strings.Contains(value.(*GisRegionItem).typelst, regtype)
	}, func(k0, v0, k1, v1 interface{}) bool {
		return v0.(*GisRegionItem).sn < v1.(*GisRegionItem).sn
	}, func(key, val interface{}) bool {
		rval = append(rval, val.(*GisRegionItem))
		return true
	})
	return rval
}

/*
*

	type:1   // 如果找不到返回nil, rval[0] := 区域id, rval[1] := attr
	  args[0], args[1], l, b
	  args[2]: 区域类型, 如果没有指定不进行过滤
	type:2  // 判断是否在某个区域内, nil: 不存在该区域;  true: 在区域内返回, false：不在返回
	  args[0] 区域id
	  args[1], args[2]: l, b
*/
func (this *GisRegionPlg) GetValue(itype int, args ...interface{}) interface{} {
	if itype == 1 {
		if len(args) < 2 {
			return nil
		}
		var regtype string
		l, _ := args[0].(float64)
		b, _ := args[1].(float64)
		if l == 0 || b == 0 {
			return nil
		}
		if len(args) >= 3 {
			regtype, _ = args[2].(string)
		}
		lst := this.GetRegionlst(regtype)
		for i := 0; i < len(lst); i++ {
			itm := lst[i]
			if itm.state == 1 {
				region := itm.region
				if region != nil && region.IsValid() {
					if region.Contain(l, b) {
						rval := make([]interface{}, 2)
						rval[0] = itm.Id
						rval[1] = itm.attr
						return rval
					}
				}
			}
		}
		return nil
	}
	return nil
}

func (this *GisRegionPlg) ReloadData() {
	t0 := time.Now()
	succ_n := 0
	defer func() {
		this.last_reload_msg = fmt.Sprintf("[%s]执行重载, 成功加载:%d, 有效区域数据:%d，耗时:%s", utils.NowString(), succ_n, this.regionlst.Count(), utils.DurationStr(time.Since(t0)))
	}()

	if len(this.localfile) > 0 && utils.FileIsExists(this.localfile) {
		if _, ok := this.reloadFromfile(this.localfile); ok {
			this.md5sumstr = utils.MD5SumFile(this.localfile)
			return
		}
	}
	if len(this.url) > 0 {
		err := this.DownLoad()
		if err != nil {
			this.LogWarnMessage(1, "%s", err.Error())
			this.last_warn = err.Error()
		} else {
			this.reloadFromfile(this.localfile)
			this.md5sumstr = utils.MD5SumFile(this.localfile)
		}
	}

}

func (this *GisRegionPlg) reloadFromfile(filename string) (succn int, ok bool) {
	json, err := wrapper.NewSVFromFile(filename)
	if err != nil {
		this.last_warn = fmt.Sprintf("[%s]数据解析异常:%s, 忽略加载", utils.NowString(), err.Error())
		return 0, false
	}

	if json != nil && json.IsObject() {
		json = json.ValueByName("list")
	}

	if json == nil || json.Count() == 0 {
		this.last_warn = fmt.Sprintf("[%s]区域数据为空, 忽略加载", utils.NowString())
		return 0, false
	}

	/*
		[
		  {"sn":0, "id":"上海", "state":1, "typelst":"[corsplus][vrs]", "attr":{"default-selflag":1, "allow-sellst":"1,2,3,4"}, "region":"121.082 31.622,122.115 31.678,122.225 30.531,120.731 30.682,120.687 31.116,120.709 31.153"}
		]
	*/
	sn := 0
	json.Range(func(key string, val *wrapper.SuperValue) bool {
		id := val.StringByName("id", "")
		if len(id) == 0 {
			return true
		}

		reg := this.checkGetItem(id)
		reg.state = val.Int8ByName("state", 1)
		reg.sn = val.IntByName("sn", sn)
		reg.setRegionStr(val.StringByName("region", ""))
		reg.typelst = val.StringByName("typelst", "")
		str := val.StringByName("attr", "")
		if reg.attrstr != str {
			reg.attrstr = str
			attrconf := val.ValueByName("attr")
			if attrconf == nil {
				reg.attr = gobase.NewStrMap()
			} else {
				attr := gobase.NewStrMap()
				if attrconf.IsObject() {
					attrconf.Range(func(key string, val *wrapper.SuperValue) bool {
						attr[strings.ToLower(key)] = val.String()
						return true
					})
				} else {
					str := attrconf.String()
					attr.ParseKVPairsEx(str, "=", ";", true)
				}
				reg.attr = attr
			}
		}
		reg.loadflag = 1
		sn = reg.sn + 1
		succn++
		return true
	})

	this.regionlst.Range(func(key, value interface{}) bool {
		itm := value.(*GisRegionItem)
		if itm.loadflag == 0 {
			this.regionlst.Remove(key)
		} else {
			itm.loadflag = 0
		}
		return true
	})
	return succn, true
}

func (this *GisRegionPlg) DownLoad() error {
	t0 := time.Now()

	if len(this.localfile) > 0 && len(this.url) > 0 {
		buf, err := PrjHttpClt.GetUrl(this.url)
		if err != nil {
			return err
		}
		if len(buf) > 0 {
			tmp := this.localfile + ".tmp"
			utils.DeleteFileNameExt(tmp)
			utils.ForceCreateFilePath(tmp)
			utils.RewriteFile(tmp, buf)

			if utils.FileIsExists(this.localfile) {
				bkfile := this.localfile + ".bk"
				err = os.Rename(this.localfile, bkfile)
				if err != nil {
					return fmt.Errorf("备份正式文件异常:%s", err.Error())
				}
			}

			err = os.Rename(tmp, this.localfile)
			if err != nil {
				return fmt.Errorf("重命名到正式配置文件异常:%s", err.Error())
			}
			this.LogInfoMessage(1, "[%s]执行下载内插基础数据，耗时:%s", utils.NowString(), utils.DurationStr(time.Since(t0)))
			return nil
		} else {
			return fmt.Errorf("%s, 返回数据为空", this.url)
		}

	} else {
		return fmt.Errorf("缺少参数, 不能进行下载")
	}
}

/*
*

	{
	  "id": "corsplus-region",
	  "type": "gnss.vrs.region",
	  "conf": {
	     "url":"http://119.96.223.225:3001/fs/conf/corsplus/region.conf",
	     "local-file":"corsplus-region.conf",
	     "reload-cron-exp":"0 0/1"
	  }
	}

[

	{"sn":0, "id":"上海", "state":1, "types":"[corsplus][vrs]", "attr":{"default-selflag":1, "allow-sellst":"1,2,3,4"}, "region":"121.082 31.622,122.115 31.678,122.225 30.531,120.731 30.682,120.687 31.116,120.709 31.153"}

]
*/
func (this *GisRegionPlg) ReloadConf(conf *wrapper.SuperValue) error {
	if conf == nil {
		return nil
	}

	if this.msglst == nil {
		this.msglst = utils.NewSyncCycleList()
		this.msgShowLvl = 0
	}
	this.msgShowLvl = conf.Int8ByName("msgshowlvl", this.msgShowLvl)

	isChanged := false
	conf.CheckStrFunc("url", func(strv string) {
		strv = utils.ReplacePlaceholder(strv, "$$", "$$")
		if this.url != strv {
			this.url = strv
			isChanged = true
		}
	})

	conf.CheckStrFunc("local-file", func(strv string) {
		if this.localfile != strv {
			this.localfile = strv
			isChanged = true
		}
	})

	if isChanged {
		this.ReloadData()
	}

	utils.DefaultPatrolTask().AddTask(this.PlugId, time.Second*30, func(id interface{}, args ...interface{}) {
		//utils.DefaultWorkers().PostTaskFunc(this, this.innerWork)
	})
	return nil
}

func (this *GisRegionPlg) Close() (err error) {
	this.PlgBase.Close()
	utils.DefaultPatrolTask().DelTask(this.PlugId)
	return
}

func (this *GisRegionPlg) OnHttpStatus(rec *plgnet.PlgHttpRec) (rval interface{}, errcode int, err error) {
	itype := rec.QryAsInt("type", 0)
	if itype == 99 {
		json := wrapper.NewSVObject()
		json.SetKeyValue("localfile", this.localfile)
		json.SetKeyValue("url", this.url)
		json.SetKeyValue("region-cnt", this.regionlst.Count())
		json.SetKeyValue("last-warn", this.last_warn)
		json.SetKeyValue("last-reload-msg", this.last_reload_msg)
		json.SetKeyValue("md5file", this.md5sumstr)
		json.SetKeyValues("now", utils.NowString())
		if rec.QryAsInt("json", 0) == 1 {
			PrjHttpUtils.ResponseAsJSON(rec.RespW, json.AsJSONStringEx(false, true))
		} else {
			PrjHttpUtils.JSON2TableRec(rec.RespW, "", json.AsJSONString(false))
		}
	} else if itype == 98 {
		rec.ResponseSyncListNEx(this.msglst)
	} else if itype == 97 {
		lvl := int8(rec.IntByName("lvl", -1))
		if lvl != -1 {
			this.msgShowLvl = lvl
		}
		rec.ResponseJSONValues("code", 0, "lvl", this.msgShowLvl)
	} else if itype == 96 {
		err := this.DownLoad()
		if err != nil {
			rec.ResponseJSONValues("code", -1, "msg", err.Error())
		}
		this.ReloadData()
		rec.ResponseJSONValues("code", 0)
	} else if itype == 95 {
		this.ReloadData()
		rec.ResponseJSONValues("code", 0)
	} else if itype == 90 {
		this.ReloadData()
		rec.ResponseJSONValues("code", 0)
	} else if itype == 1 {
		json := wrapper.NewSVArray()
		searchval := rec.QryAsString("searchval", "")
		sn := 0
		maxn := rec.QryAsInt("n", 100)
		this.regionlst.ListRangeV2(func(key, val interface{}) bool {
			pojo := val.(*GisRegionItem)
			ok := true
			if len(searchval) > 0 {
				ok = strings.Contains(pojo.Id, searchval) || strings.Contains(pojo.attrstr, searchval)
			}
			return ok
		}, func(k0, v0, k1, v1 interface{}) bool {
			p0 := v0.(*GisRegionItem)
			p1 := v1.(*GisRegionItem)
			if p0.sn != p1.sn {
				return p0.sn < p1.sn
			}
			return p0.Id < p1.Id
		}, func(key, val interface{}) bool {
			sn++
			pojo := val.(*GisRegionItem)
			itm := json.AppendObject()
			itm.SetKeyValue("sn", sn)
			itm.SetKeyValue("id", pojo.Id)
			attr := pojo.attr
			if attr == nil {
				itm.SetKeyValue("attr", "")
			} else {
				itm.SetKeyValue("attr", attr.Encode("=", ";"))
			}
			itm.SetKeyValue("region", pojo.regionstr)
			itm.SetKeyValue("state", pojo.state)
			itm.SetKeyValue("typelst", pojo.typelst)
			itm.SetKeyValue("warn", pojo.warn)
			return sn < maxn
		})

		if rec.QryAsInt("json", 0) == 1 {
			PrjHttpUtils.ResponseAsJSON(rec.RespW, json.AsJSONStringEx(false, false))
		} else {
			PrjHttpUtils.JSON2Table(rec.RespW, "", json.AsJSONString(false))
		}
	} else {
		var sb utils.BytesBuilder
		PrjHttpUtils.AppendIndexBody(&sb, "status", "status?type=99")
		PrjHttpUtils.AppendIndexBody(&sb, "msglist", "status?type=98&n=100&searchval=")
		PrjHttpUtils.AppendIndexBody(&sb, "msgshowlvl", "status?type=97&lvl=")
		PrjHttpUtils.AppendIndexBody(&sb, "downloadAndReload", "status?type=96")
		PrjHttpUtils.AppendIndexBody(&sb, "reload", "status?type=95")
		PrjHttpUtils.AppendIndexBody(&sb, "lst", "status?type=1&n=100&searchval=")

		PrjHttpUtils.ResponseBodyHtml(rec.RespW, sb.String())
	}
	return
}

// msglvl: 值越大信息约重要
func (this *GisRegionPlg) LogInfoMessage(msglvl int8, s string, args ...interface{}) {
	if msglvl < this.msgShowLvl {
		return
	}
	this.msglst.LogMsg(s, args...)
	utils.Log.Msgf(this.PlugId, utils.LogLV_INFO, 1, s, args...)
}

func (this *GisRegionPlg) LogWarnMessage(msglvl int8, s string, args ...interface{}) {
	if msglvl < this.msgShowLvl {
		return
	}
	this.msglst.LogMsg(s, args...)
	utils.Log.Msgf(this.PlugId, utils.LogLV_WARN, 1, s, args...)
}

func CreateGnssRegionPlgFunc(args ...interface{}) (rval interface{}, err error) {
	obj := &GisRegionPlg{}

	if len(args) > 0 {
		if str, ok := args[0].(string); ok {
			obj.PlugId = str
		}
	}
	rval = obj
	return
}

func init() {
	utils.RegisterCreate("gis.region", CreateGnssRegionPlgFunc)
}
